﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Mirror.CliMetadata.Tables;
using Kokomo.Mirror.CliMetadata.Signatures;
using Kokomo.Mirror.Ia32;

namespace Kokomo.Mirror.CliMetadata
{
	class CliConstructorInfo : ConstructorInfo, IExtendedMethodInfo
	{
		private MethodDefRow _methodDefRow;
		//private MethodRefSignature _signature;

		public CliConstructorInfo(
			MethodDefRow methodDefRow,
			CliMemberBasicInfo basicInfo,
			string name
			)
		{
			this._methodDefRow = methodDefRow;
			this.basicInfo = basicInfo;
			this._name = name;
			//this._signature = (MethodRefSignature)methodDefRow.Signature.Signature;
			//this._parameters = new Lazy<ParameterInfo[]>(this.LoadParameters);
			//this._customAttributeData = new Lazy<IList<CustomAttributeData>>(this.LoadCustomAttributeData);
			//this._body = new Lazy<MethodBody>(this.LoadMethodBody);
		}

		private CliMemberBasicInfo basicInfo;

		protected MethodDefSignature GetSignature()
		{
			return (MethodDefSignature)this.basicInfo.module.GetSignature(this._methodDefRow.Signature);
		}

		#region Attributes

		//private Lazy<IList<CustomAttributeData>> _customAttributeData;
		private IList<CustomAttributeData> LoadCustomAttributeData()
		{
			return this.basicInfo.module.GetCustomAttributesFor(this.MetadataToken);
		}
		public override IList<CustomAttributeData> GetCustomAttributesData()
		{
			//return this._customAttributeData.Value;
			return this.LoadCustomAttributeData();
		}
		#endregion

		public override int MetadataToken
		{
			get { return this.basicInfo.token; }
		}

		public override Module Module
		{
			get { return this.basicInfo.module; }
		}

		public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override MethodAttributes Attributes
		{
			get { return this._methodDefRow.Flags; }
		}

		public override MethodImplAttributes GetMethodImplementationFlags()
		{
			return this._methodDefRow.ImplFlags;
		}

		//private Lazy<ParameterInfo[]> _parameters;
		private ParameterInfo[] LoadParameters()
		{
			ParameterInfo returnParameter;
			return this.basicInfo.module.QueryParameters(
				CodedIndex.FromToken(this.MetadataToken).RowNumber,
				this,
				this.GetSignature(),
				out returnParameter
				);
		}
		public override ParameterInfo[] GetParameters()
		{
			//return this._parameters.Value;
			return this.LoadParameters();
		}

		public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override RuntimeMethodHandle MethodHandle
		{
			get { throw new NotImplementedException(); }
		}

		public override Type DeclaringType
		{
			get { return this.basicInfo.declaringType; }
		}

		public override object[] GetCustomAttributes(Type attributeType, bool inherit)
		{
			throw new NotImplementedException();
		}

		public override object[] GetCustomAttributes(bool inherit)
		{
			throw new NotImplementedException();
		}

		public override bool IsDefined(Type attributeType, bool inherit)
		{
			return this.basicInfo.module.IsAttributeDefinedOn(this.MetadataToken, attributeType, inherit);
		}

		private string _name;
		public override string Name
		{
			get { return this._name; }
		}

		public override Type ReflectedType
		{
			get { return this.basicInfo.reflectedType; }
		}

		//private Lazy<MethodBody> _body;
		private MethodBody LoadMethodBody()
		{
			if (this._methodDefRow.ImplFlags.HasFlag(MethodImplAttributes.Native)) return null;

			Type[] genericTypeArgs = null;
			if ((object)this.DeclaringType != null)
				genericTypeArgs = this.DeclaringType.GetGenericArguments();

			return this.basicInfo.module.LoadMethodBody(
				this,
				(uint)this._methodDefRow.Rva
				);
		}
		public override MethodBody GetMethodBody()
		{
			return this.LoadMethodBody();
			//return this._body.Value;
		}

		public uint Rva
		{
			get { return this._methodDefRow.Rva; }
		}
		public NativeProcedureInfo GetNativeImplementation()
		{
			if (!this._methodDefRow.ImplFlags.HasFlag(MethodImplAttributes.Native))
				throw new InvalidOperationException("Must have a native implementation");

			uint rva = this.Rva;
			if (rva == 0) return null;

			return this.basicInfo.module.peModule.GetProcedureAtRva(rva);
		}

		public override Type[] GetGenericArguments()
		{
			return Type.EmptyTypes;
		}

		public ConstructorInfo Reflect()
		{
			Type type = this.DeclaringType.Reflect();
			ParameterInfo[] parameters = this.GetParameters();
			Type[] parameterTypes = Array.ConvertAll(parameters, p => p.ParameterType.Reflect());

			ConstructorInfo constructor = type.GetConstructor(
				BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
				Type.DefaultBinder,
				parameterTypes,
				null
				);

			return constructor;
		}
	}
}
